home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Printer Drivers… / LaserWriter--custom dialogs / Generic LaserWriter.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-15  |  34.9 KB  |  1,206 lines  |  [TEXT/MPS ]

  1. /*-----------------------------------------------------------------------------
  2.  
  3.     Generic LaserWriter.c
  4.     
  5.      This part of the driver demonstrates how to implement custom
  6.      old-API dialogs for a PostScript driver.
  7.         
  8.      12/18/93 - dmh - Removed IIg-dependencies for b3.
  9.       9/13/93 - dmh - Updated for the b2 seed.
  10.       9/14/93 - dmh - Disabled the PostScript log which was created in
  11.                       the Extensions folder.
  12.       2/11/94 - dmh - Added custom old-API dialogs.
  13.       3/08/94 - lll - Fixed DriverConvertPrintRecordTo so that Orientation is handled correctly
  14.       4/20/94 - dmh - Aaaaah!  Forgotten style dialog raises havok.  News at 11.
  15.       6/20/94 - dmh - Removed the PS file log code, for clarity.
  16.       8/28/94 - dmh - Finalized for SDK.
  17.       6/14/96 - cn  - Updated to support Universal Interfaces 2.1.
  18.  
  19.     © 1991-1996 Apple Computer Inc.
  20.     
  21. -----------------------------------------------------------------------------*/
  22.  
  23. #include <GXPrinterDrivers.h>
  24. #include <GXExceptions.h>
  25. #include <TextUtils.h>
  26. #include <Packages.h>
  27. #include <Resources.h>
  28. #include <Errors.h>
  29. #include <StandardFile.h>
  30.  
  31. #define kDeviceByte        (char) 0x88        /* Our wDev ID */
  32.  
  33. #define kJob_CopiesEditText            5
  34. #define kJob_AllPagesRadio            7
  35. #define kJob_FromPageRadio            8
  36. #define kJob_FromPageEditText        9
  37. #define kJob_ToPageEditText            11
  38. #define kJob_AutoFeedRadio            12
  39. #define kJob_ManualFeedRadio        13
  40. #define kJob_PrintToDiskCheckBox    15
  41. #define kJob_lineFrill_1            16
  42. #define kJob_lineFrill_2            17
  43.  
  44. #define kStl_lineFrill_1            4
  45. #define kStl_lineFrill_2            5
  46.  
  47. extern long A5Size (void);
  48. extern void A5Init (void *);
  49.  
  50. void        InitJobDialogControls(DialogPtr theDialog, THPrint thePrintHdl);
  51. OSErr        PrepForFileWriting(void);
  52. pascal void    DriverJobItemHandler(DialogPtr theDialog, short item);
  53. pascal Boolean DriverJobFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);
  54. void        ToggleControl(void **itemH);
  55. void        SetTheControlValue(void **itemH, short desiredValue);
  56. void        UpdateJobDlgPrintRec(DialogPtr theDialog);
  57. OSErr        StoreJobCollectionItem(void *collectItem, long collectSize,
  58.                                    OSType collectType, long collectID);
  59.  
  60. long GetCurrentA5(void) = 0x200D;    // move.l a5, d0
  61.  
  62.  
  63. // A structure for our context data.
  64.  
  65. typedef struct MyContextDataRec {
  66.     long        appA5;
  67.     Boolean        inStyleDialog;
  68. } MyContextDataRec, *MyContextDataPtr, **MyContextDataHdl;
  69.  
  70.  
  71.  
  72. /*    ______________________________________________________________
  73.  
  74.     DriverInitialize -
  75.     
  76.     This routine is a total override of the gxInitialize
  77.     message. In here, we store the application's A5 reference
  78.     for later, and set up our driver's A5 world, so we can
  79.     access global data.
  80.     
  81.     WHY THIS IS IMPORTANT:
  82.     
  83.     If you do not have the application's A5 reference, you won't
  84.     be able to support application callbacks, which occur when
  85.     you totally (and properly) override PrDlgMain.  If you're
  86.     going to perform a total override of PrDlgMain, you need
  87.     the app's A5 value.  This is probably the best place to get
  88.     it.  Note that we get and store the A5 reference in our
  89.     instance context BEFORE calling NewMessageGlobals.  Once
  90.     NewMessageGlobals is called, the app's A5 reference is
  91.     replaced with ours.
  92.  
  93.     ______________________________________________________________    */
  94.  
  95. OSErr DriverInitialize(void)
  96. {
  97.     OSErr                err;
  98.     MyContextDataHdl    contextData;
  99.     
  100.     contextData = (MyContextDataHdl) NewHandleSys(sizeof(MyContextDataRec));
  101.     nrequire(err = MemError(), NewHandleSys_Failed);
  102.  
  103.     (*contextData)->appA5 = GetCurrentA5();
  104.     (*contextData)->inStyleDialog = false;
  105.     SetMessageHandlerInstanceContext(contextData);
  106.  
  107.     err = NewMessageGlobals(A5Size(), A5Init);
  108.  
  109. NewHandleSys_Failed:    
  110.     return err;
  111. }
  112.  
  113.  
  114. /*    ______________________________________________________________
  115.  
  116.     DriverShutDown -
  117.     
  118.     This routine is a total override of the gxShutDown
  119.     message.
  120.     
  121.     WHY THIS IS IMPORTANT:
  122.     
  123.     It cleans up after our DriverInitialize override.
  124.  
  125.     ______________________________________________________________    */
  126.  
  127. OSErr DriverShutDown(void)
  128. {
  129.     MyContextDataHdl    contextData;
  130.     
  131.     contextData = GetMessageHandlerInstanceContext();
  132.  
  133.     if (contextData != nil)
  134.         DisposHandle((Handle) contextData);
  135.  
  136.     DisposeMessageGlobals();
  137.     return noErr;
  138. }
  139.  
  140.  
  141. /*    ______________________________________________________________
  142.  
  143.     DriverPrDlgMain -
  144.     
  145.     This routine is a total override of the gxPrDlgMain message in
  146.     the Job dialog case, and a partial override in the Style
  147.     dialog case. In here, we perform displaying and handling of
  148.     the old-API print (Job and Style) dialogs.
  149.  
  150.     WHY THIS IS IMPORTANT:
  151.     
  152.     If you need to customize the old-API dialogs for a PostScript
  153.     driver, you cannot simply add a 'dctl' and 'DITL' resource
  154.     like you can for a raster or vector driver.  The default
  155.     PostScript implementation will ignore your resources.  Also,
  156.     because of everything the default PostScript implementation
  157.     does with its own dctl behind the scenes, you cannot partially
  158.     override this message, switch in your dialog somehow and
  159.     live to tell about it.
  160.     
  161.     You must completely override this message if you're doing
  162.     custom old-API dialogs for a PostScript driver (As in the Job
  163.     dialog case for this driver.  And, you need the application's
  164.     A5 reference to pull this off. See the DriverInitialize routine
  165.     above for details on getting that reference.
  166.  
  167.     ______________________________________________________________    */
  168.  
  169. OSErr DriverPrDlgMain(THPrint hPrint, PDlgInitProcPtr pDlgInit,
  170.                       Boolean *okPressed)
  171. {
  172.     OSErr               err = noErr;
  173.     TPPrDlg             prDlgPtr;
  174.     GrafPtr                oldPort;
  175.     short                  itemHit;
  176.     long                oldA5, appA5;
  177.      MyContextDataHdl    contextData;
  178.  
  179. /*
  180.     Retrieve the data we stored in our instance context.
  181.     This data contains a reference to the app's A5 world,
  182.     and a boolean indicating wether we're putting up the
  183.     Style or Job dialogs.  If we're being called for the
  184.     Style dialog, simply forward the message and return.
  185.     That's because this driver doesn't modify the default
  186.     PS style dialog.
  187. */
  188.  
  189.     contextData = GetMessageHandlerInstanceContext();
  190.  
  191.     if ((*contextData)->inStyleDialog)
  192.     {
  193.         err = Forward_GXPrDlgMain(hPrint, pDlgInit, okPressed);
  194.         nrequire(true, ClearContextFlag);
  195.     }
  196.  
  197.  
  198. /*
  199.     Retrieve the application's A5 reference that we stored
  200.     in our instance context.  If it's valid (non-nil),
  201.     save our A5, switch in the app's, and call the initProc to
  202.     get the print dialog pointer.
  203.  
  204.     If the application added any items to our dialog, it will
  205.     do some initialization here, and may need it's A5 to be
  206.     valid to do so.  After we return, restore our A5 value so
  207.     that we can access our globals and so forth.
  208. */
  209.  
  210.     appA5 = (*contextData)->appA5;
  211.   
  212.     if (appA5) 
  213.         oldA5 = SetA5(appA5);
  214.  
  215.     prDlgPtr = (*pDlgInit)(hPrint);
  216.  
  217.     if (appA5) 
  218.         SetA5(oldA5);
  219.  
  220.     require(prDlgPtr, DialogInit);
  221.  
  222.   
  223. /*
  224.     Now that we have the print dialog, save the current port,
  225.     and show the dialog.
  226. */
  227.     
  228.     GetPort(&oldPort);
  229.     ShowWindow((WindowPtr) prDlgPtr);
  230.  
  231.  
  232. /*
  233.     Here's the guts of this routine.  We simply use ModalDialog
  234.     to handle things.  Notice that we restore the app's A5
  235.     register around the ModalDialog procedure.  Again, this is
  236.     a likely place for an app to handle appended dialog items,
  237.     and it may require a valid A5 to do so.
  238.  
  239.     Take a look at DriverPrJobInit or DriverPrStlInit to see
  240.     how we set up both the filterProc and the itemProc before
  241.     we ever get here.  You may be wondering how an application
  242.     could get control since we're doing that.  The app would
  243.     replace our filter and item procs with its own, and then
  244.     pass control back to us as needed (for handling our items).
  245.     
  246.     Anyway, we keep looping until the fDone flag is set.
  247. */
  248.  
  249.    do
  250.    {
  251.        SetPort((GrafPtr) prDlgPtr);
  252.      
  253.        if (appA5) 
  254.            oldA5 = SetA5(appA5);
  255.  
  256.        ModalDialog(prDlgPtr->pFltrProc, &itemHit);
  257.        prDlgPtr->pItemProc((DialogPtr) prDlgPtr, itemHit);
  258.     
  259.        if (appA5)
  260.             SetA5(oldA5);
  261.      
  262.    } while (!prDlgPtr->fDone);
  263.  
  264.    *okPressed = prDlgPtr->fDoIt;
  265.    SetPort(oldPort);
  266.  
  267. /*
  268.     We're all done with the grunt work.  Now clean up after
  269.     ourselves and validate the dialog if the user pressed OK.
  270.     Finally, clear the idle proc to make sure that an old
  271.     one that was saved in the document doesn't get used by
  272.     mistake!
  273. */
  274.  
  275.     CloseDialog((DialogPtr) prDlgPtr);
  276.     DisposHandle(((DialogPeek) prDlgPtr)->items);
  277.     DisposePtr((Ptr) prDlgPtr);
  278.  
  279. /*
  280.     If the user pressed OK, call PrValidate.
  281. */
  282.  
  283.     if (*okPressed)
  284.     {
  285.         PrValidate(hPrint);
  286.         (*hPrint)->prJob.pIdleProc = nil;
  287.     }
  288.  
  289. ClearContextFlag:
  290.     contextData = GetMessageHandlerInstanceContext();
  291.     (*contextData)->inStyleDialog = false;
  292.     SetMessageHandlerInstanceContext(contextData);
  293.  
  294. DialogInit:
  295.     return err;
  296. }
  297.  
  298.  
  299. /*    ______________________________________________________________
  300.  
  301.     DriverPrStlInit -
  302.     
  303.     This routine is a partial override for the gxPrStlInit message.
  304.  
  305.     WHY THIS IS IMPORTANT:
  306.     
  307.     This is where we set our flag that tells PrDlgMain that we're
  308.     putting up the Style dialog.
  309.  
  310.     ______________________________________________________________    */
  311.  
  312. OSErr DriverPrStlInit(THPrint aPrintHdl, TPPrDlg *aPrDlg)
  313. {
  314.      MyContextDataHdl    contextData;
  315.  
  316.     contextData = GetMessageHandlerInstanceContext();
  317.     (*contextData)->inStyleDialog = true;
  318.     SetMessageHandlerInstanceContext(contextData);
  319.  
  320.     return Forward_GXPrStlInit(aPrintHdl, aPrDlg);
  321. }
  322.  
  323.  
  324. /*    ______________________________________________________________
  325.  
  326.     DriverPrJobInit -
  327.     
  328.     This routine is a total override for the gxPrJobInit message.
  329.     In here, we create our print dialog pointer, and initialize
  330.     it and the print handle as we see fit.
  331.  
  332.     WHY THIS IS IMPORTANT:
  333.     
  334.     This is where we install our ModalDialog filter proc and our
  335.     job item handler.
  336.     
  337.     You must completely override this message if you're changing
  338.     the default old-API dialogs in any way. If you try to perform
  339.     a partial override, the default PS implementation is going to
  340.     give you a good swift kick.
  341.  
  342.     ______________________________________________________________    */
  343.  
  344. OSErr DriverPrJobInit(THPrint aPrintHdl, TPPrDlg *aPrDlg)
  345. {
  346.     OSErr               err = noErr;
  347.     short                oldResFile = CurResFile();
  348.      MyContextDataHdl    contextData;
  349.  
  350. /*
  351.    Indicate to our PrDlgMain override that we are displaying
  352.    the Print dialog.
  353. */
  354.  
  355.     contextData = GetMessageHandlerInstanceContext();
  356.     (*contextData)->inStyleDialog = false;
  357.     SetMessageHandlerInstanceContext(contextData);
  358.  
  359.  
  360. /*
  361.    Validate our print record, then create our dialog pointer.
  362. */
  363.  
  364.    PrValidate(aPrintHdl);
  365.    *aPrDlg = (TPPrDlg) NewPtrClear(sizeof(TPrDlg));
  366.    require_action(*aPrDlg, NewPtrClear_Failed, err = MemError(););
  367.  
  368.  
  369. /*
  370.    This next line forces manual feed mode off for our driver.
  371.    The rest is standard print dialog setup stuff.
  372. */
  373.  
  374.    (*aPrintHdl)->prStl.feed = 2;
  375.  
  376.    (*aPrDlg)->pFltrProc = (ModalFilterProcPtr) DriverJobFilterProc;
  377.    (*aPrDlg)->pItemProc = (PItemProcPtr) DriverJobItemHandler;
  378.    (*aPrDlg)->hPrintUsr = aPrintHdl;
  379.    (*aPrDlg)->fDoIt = false;
  380.    (*aPrDlg)->fDone = false;
  381.  
  382.  
  383. /*
  384.    Load our dialog, using the dialog pointer we just created.
  385.    Initialize our dialog's controls so they reflect what's
  386.    currently in our print record.  You need the drivers resfile
  387.    or the pict in the print dialog will be scrunged in some apps
  388. */
  389.  
  390.     UseResFile(GXGetMessageHandlerResFile());
  391.     GetNewDialog(gxJobDialogResID, *aPrDlg, (WindowPtr) -1);
  392.     InitJobDialogControls((DialogPtr) *aPrDlg, aPrintHdl);
  393.  
  394.     UseResFile(oldResFile);
  395.  
  396. NewPtrClear_Failed:
  397.    return err;
  398. }
  399.  
  400.  
  401. /*    ______________________________________________________________
  402.  
  403.     DriverJobFilterProc -
  404.     
  405.     This routine is our ModalDialog filter proc for our Print
  406.     dialog.  It gets called during the PrDlgMain cycle.
  407.  
  408.     WHY THIS IS IMPORTANT:
  409.     
  410.     This is where we can handle update events for user items, and
  411.     that sort of thing.
  412.  
  413.     ______________________________________________________________    */
  414.  
  415. pascal Boolean DriverJobFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
  416. {
  417.     short        itemKind;
  418.     Handle        itemHdl;
  419.     Rect        itemRect;
  420.  
  421. #pragma unused(itemHit);
  422.  
  423. /*
  424.     If there's an update event, draw a border around the default
  425.     button, and draw our dialog's line frills.
  426. */
  427.  
  428.     switch (theEvent->what)
  429.     {
  430.         case updateEvt:
  431.         
  432.             SetPort(theDialog);
  433.             GetDItem(theDialog, ok, &itemKind, &itemHdl, &itemRect);
  434.             PenSize(3, 3);
  435.             InsetRect(&itemRect, -4, -4);
  436.             FrameRoundRect(&itemRect, 16, 16);
  437.             PenSize(1, 1);
  438.  
  439.             GetDItem(theDialog, kJob_lineFrill_1, &itemKind, &itemHdl, &itemRect);
  440.             MoveTo(itemRect.left, itemRect.top);
  441.             LineTo(itemRect.right, itemRect.top);
  442.             GetDItem(theDialog, kJob_lineFrill_2, &itemKind, &itemHdl, &itemRect);
  443.             MoveTo(itemRect.left, itemRect.top);
  444.             LineTo(itemRect.right, itemRect.top);
  445.             break;
  446.     }
  447.     
  448.     return false;
  449. }
  450.  
  451.  
  452. /*    ______________________________________________________________
  453.  
  454.     DriverJobItemHandler -
  455.     
  456.     This routine handles item hits in our job dialog.  It is
  457.     installed in the print dialog by DriverPrJobInit, and will be
  458.     called during the PrDlgMain cycle.
  459.  
  460.     WHY THIS IS IMPORTANT:
  461.     
  462.     This is the routine that handles all hits in our Print dialog.
  463.  
  464.     ______________________________________________________________    */
  465.  
  466. pascal void DriverJobItemHandler(DialogPtr theDialog, short item)
  467. {
  468.     OSErr        err = noErr;
  469.     TPPrDlg        thePrDlg = (TPPrDlg) theDialog;
  470.     short        itemKind;
  471.     Handle        itemHdl;
  472.     Rect        itemRect;
  473.  
  474.     switch (item)
  475.     {
  476. /*
  477.     Was OK pressed?  See if we're printing to disk.  If so, prompt
  478.     the user for a location.  If they cancel, continue with the print
  479.     dialog. Otherwise, update the print record to reflect the current
  480.     dialog settings, and signal that we're done and we're going to
  481.     "do it."
  482.     
  483.     Note that PrepForFileWriting adds our "print to file" collection
  484.     items to the current job for us.
  485. */
  486.         case ok:
  487.             GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  488.             if (GetCtlValue((ControlHandle) itemHdl) != 0)
  489.                 err = PrepForFileWriting();
  490.  
  491.             if (!err)
  492.             {
  493.                 thePrDlg->fDoIt = true;
  494.                 thePrDlg->fDone = true;
  495.                 UpdateJobDlgPrintRec(theDialog);
  496.             }
  497.             break;
  498. /*
  499.     Was Cancel pressed?  We're done but we're not "doing it."
  500. */
  501.         case cancel:
  502.             thePrDlg->fDoIt = false;
  503.             thePrDlg->fDone = true;
  504.             break;
  505. /*
  506.     Were the "All" or "From" page radio buttons pressed? Select them
  507.     appropriately.
  508. */
  509.         case kJob_AllPagesRadio:
  510.         case kJob_FromPageRadio:
  511.             GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  512.             SetTheControlValue((void **) itemHdl, (item == kJob_AllPagesRadio)? 1: 0);
  513.             GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl, &itemRect);
  514.             SetTheControlValue((void **) itemHdl, (item == kJob_FromPageRadio)? 1: 0);
  515.             break;
  516. /*
  517.     Were the "From" or "To" editText items hit?  Select the "From"
  518.     radio button, and deselect the "All" radio button.
  519. */
  520.         case kJob_FromPageEditText:
  521.         case kJob_ToPageEditText:
  522.             GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  523.             SetTheControlValue((void **) itemHdl, 0);
  524.             GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl, &itemRect);
  525.             SetTheControlValue((void **) itemHdl, 1);
  526.             break;
  527. /*
  528.     Were the "Automatic" or "Manual" feed radio buttons hit? Select
  529.     them appropriately.
  530. */
  531.         case kJob_AutoFeedRadio:
  532.         case kJob_ManualFeedRadio:
  533.             GetDItem(theDialog, kJob_AutoFeedRadio, &itemKind, &itemHdl, &itemRect);
  534.             SetTheControlValue((void **) itemHdl, (item == kJob_AutoFeedRadio)? 1: 0);
  535.             GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl, &itemRect);
  536.             SetTheControlValue((void **) itemHdl, (item == kJob_ManualFeedRadio)? 1: 0);
  537.             break;
  538. /*
  539.     Was the "Print to disk" checkBox hit?  Toggle its value.
  540. */
  541.         case kJob_PrintToDiskCheckBox:
  542.             GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  543.             ToggleControl((void **) itemHdl);
  544.             break;
  545.     }
  546. }
  547.  
  548.  
  549. /*    ______________________________________________________________
  550.  
  551.     UpdateJobDlgPrintRec -
  552.     
  553.     This routine updates the job dialog's print record so that
  554.     it reflects the current dialog settings.  This routine is only
  555.     called when a user "OKs" the job dialog. It is the opposite of
  556.     InitJobDialogControls.
  557.  
  558.     WHY THIS IS IMPORTANT:
  559.     
  560.     This is how we transfer all the button clicks we handled in
  561.     DriverJobItemHandler into settings in the driver's print
  562.     record.
  563.  
  564.     ______________________________________________________________    */
  565.  
  566. void UpdateJobDlgPrintRec(DialogPtr theDialog)
  567. {
  568.     TPPrDlg        thePrDlg = (TPPrDlg) theDialog;
  569.     THPrint        printRecHdl = thePrDlg->hPrintUsr;
  570.     short        itemKind, ctlVal;
  571.     long        numCopies, fstPage, lstPage;
  572.     Handle        itemHdl;
  573.     Rect        itemRect;
  574.     Str255        pStr;
  575.  
  576. /*
  577.     Get the value of the "All pages" radio button.  If it's set,
  578.     make our page range 1 through 9999.  Otherwise, use the user's
  579.     range, (from the "From" and "To" editText fields.  Also do a
  580.     bit of sanity checking on the user's values.
  581. */
  582.  
  583.     GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  584.     ctlVal = GetCtlValue((ControlHandle) itemHdl);
  585.     if (ctlVal != 0)
  586.     {
  587.         (*printRecHdl)->prJob.iFstPage = 1;
  588.         (*printRecHdl)->prJob.iLstPage = 9999;
  589.     }
  590.     else
  591.     {
  592.         GetDItem(theDialog, kJob_FromPageEditText, &itemKind, &itemHdl, &itemRect);
  593.         GetIText(itemHdl, pStr);
  594.         StringToNum(pStr, &fstPage);
  595.         GetDItem(theDialog, kJob_ToPageEditText, &itemKind, &itemHdl, &itemRect);
  596.         GetIText(itemHdl, pStr);
  597.         StringToNum(pStr, &lstPage);
  598.         
  599.         fstPage = (fstPage < 1)? 1: fstPage;
  600.         fstPage = (fstPage > 9999)? 9999: fstPage;
  601.         lstPage = (lstPage < 1)? 1: lstPage;
  602.         lstPage = (lstPage > 9999)? 9999: lstPage;
  603.         lstPage = (lstPage < fstPage)? fstPage: lstPage;
  604.  
  605.         (*printRecHdl)->prJob.iFstPage = fstPage; 
  606.         (*printRecHdl)->prJob.iLstPage = lstPage; 
  607.     }
  608.  
  609. /*
  610.     Get the number of copies and store it in the print record
  611.     where our driver likes it.
  612. */
  613.  
  614.     GetDItem(theDialog, kJob_CopiesEditText, &itemKind, &itemHdl, &itemRect);
  615.     GetIText(itemHdl, pStr);
  616.     StringToNum(pStr, &numCopies);
  617.     (*printRecHdl)->prXInfo.iRowBytes = (numCopies > 0)? numCopies:1;
  618.  
  619.  
  620. /*
  621.     Get the "print to disk" value, and store it in the print record
  622.     where our driver likes it.  Yes, this looks a bit weird, but 120
  623.     bytes only go so far.
  624.     
  625.     Note that we disable manual feed if we're printing to disk.
  626.     If we're not printing to disk, see if we're manually feeding,
  627.     and store that info as our driver likes it.
  628. */
  629.  
  630.     GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  631.     ctlVal = GetCtlValue((ControlHandle) itemHdl);
  632.     (*printRecHdl)->prXInfo.bUlOffset = (ctlVal != 0)? 1 :0;
  633.  
  634.     if (ctlVal == 0)
  635.     {
  636.         GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl, &itemRect);
  637.         ctlVal = GetCtlValue((ControlHandle) itemHdl);
  638.         (*printRecHdl)->prStl.feed = (ctlVal != 0)? 3: 2;
  639.     }
  640.     else
  641.         (*printRecHdl)->prStl.feed = 2;
  642. }
  643.  
  644.  
  645. /*    ______________________________________________________________
  646.  
  647.     InitJobDialogControls -
  648.     
  649.     This routine initializes the controls of our dialog so that
  650.     they reflect what's in the passed print record.  This routine
  651.     is only called when the job dialog is about to be displayed.
  652.     It is the opposite of UpdateJobDlgPrintRec.
  653.  
  654.     WHY THIS IS IMPORTANT:
  655.     
  656.     This is where we synchronize the job dialog with what's in
  657.     our print record.
  658.  
  659.     ______________________________________________________________    */
  660.  
  661. void InitJobDialogControls(DialogPtr theDialog, THPrint thePrintHdl)
  662. {
  663.     short            itemKind;
  664.     Handle            itemHdl1, itemHdl2, itemHdl3, itemHdl4;
  665.     Rect            itemRect;
  666.     TPrint            *printRecPtr;
  667.     Str255            pStr;
  668.     char            oldState;
  669.  
  670. /*
  671.     Lock down the print handle so we can treat it as a pointer.
  672.     Save the lock state so that we can restore it when we exit.
  673. */
  674.     
  675.     oldState = HGetState((Handle) thePrintHdl);
  676.     HLock((Handle) thePrintHdl);
  677.     printRecPtr = *thePrintHdl;
  678.  
  679.  
  680. /*
  681.     Set up the "From… To…" editText items, and enable the appropriate
  682.     radio button.  We only enable the "All pages" radio button if
  683.     pages 1 through 9999 are selected.
  684. */
  685.     
  686.     GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl1, &itemRect);
  687.     GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl2, &itemRect);
  688.     GetDItem(theDialog, kJob_FromPageEditText, &itemKind, &itemHdl3, &itemRect);
  689.     GetDItem(theDialog, kJob_ToPageEditText, &itemKind, &itemHdl4, &itemRect);
  690.     
  691.     if ((printRecPtr->prJob.iFstPage == 1) && (printRecPtr->prJob.iLstPage == 9999))
  692.     {
  693.         SetTheControlValue((void **) itemHdl1, 1);        // Printing all pages
  694.         SetTheControlValue((void **) itemHdl2, 0);
  695.         SetIText(itemHdl3, "\p");
  696.         SetIText(itemHdl4, "\p");
  697.     }
  698.     else
  699.     {
  700.         SetTheControlValue((void **) itemHdl1, 0);        // Printing "From… To…"
  701.         SetTheControlValue((void **) itemHdl2, 1);
  702.         NumToString(printRecPtr->prJob.iFstPage, pStr);
  703.         SetIText(itemHdl3, pStr);
  704.         NumToString(printRecPtr->prJob.iLstPage, pStr);
  705.         SetIText(itemHdl4, pStr);
  706.     }
  707.  
  708. /*
  709.     Set up the number of copies editText item.  Our driver stores this
  710.     value in the prXInfo.iRowBytes field of the print record.
  711. */
  712.     
  713.     GetDItem(theDialog, kJob_CopiesEditText, &itemKind, &itemHdl1, &itemRect);
  714.     NumToString(printRecPtr->prXInfo.iRowBytes, pStr);
  715.     SetIText(itemHdl1, pStr);
  716.  
  717.  
  718. /*
  719.     Set up the "Automatic" or "Manual" Feed radio buttons.  Our
  720.     driver stores this information in the prStl.feed field of
  721.     the print record.
  722. */
  723.     
  724.     GetDItem(theDialog, kJob_AutoFeedRadio, &itemKind, &itemHdl1, &itemRect);
  725.     SetTheControlValue((void **) itemHdl1, (printRecPtr->prStl.feed == 3)? 0: 1);
  726.     GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl1, &itemRect);
  727.     SetTheControlValue((void **) itemHdl1, (printRecPtr->prStl.feed == 3)? 1: 0);
  728.  
  729.  
  730. /*
  731.     Set up the "Print to disk" checkBox.  Our driver stores this
  732.     information in the prXInfo.bUlOffset field of the print record.
  733.     Restore the print handle's lock status as we exit this routine.
  734. */
  735.     
  736.     GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl1, &itemRect);
  737.     SetTheControlValue((void **) itemHdl1, (printRecPtr->prXInfo.bUlOffset == 0)? 0: 1);
  738.  
  739.     HSetState((Handle) thePrintHdl, oldState);
  740. }
  741.  
  742.  
  743. /*    ______________________________________________________________
  744.  
  745.     PrepForFileWriting -
  746.     
  747.     This routine asks the user where to save a PostScript file.
  748.     If the user doesn't cancel, it saves the appropriate
  749.     collection items in the current job to make printing to a
  750.     file happen.
  751.  
  752.     WHY THIS IS IMPORTANT:
  753.     
  754.     The default PostScript implementation of PrJobInit installs
  755.     an item handler proc for the default old-API dialogs.  The
  756.     "print to file" StandardFile dialog is handled in that item
  757.     handling proc.  Since we completely override gxPrJobInit, the
  758.     default "print to file" file dialog code is never used.  You
  759.     must supply this routine if you completely override PrJobInit,
  760.     and you want to print to PostScript (or other) files.
  761.     
  762.     For simplicity, this file dialog only saves in one format--
  763.     PostScript® with no fonts.  It's a straightforward
  764.     CustomPutFile exercise to reproduce the default dialog if you
  765.     so desire.
  766.  
  767.     ______________________________________________________________    */
  768.  
  769. OSErr PrepForFileWriting()
  770. {
  771.     OSErr                        err = noErr;
  772.     gxFileDestinationInfo        destInfo;
  773.     gxFileFormatInfo            destFFmt;
  774.     gxFileLocationInfo            fileLocInfo;
  775.     gxFileFontsInfo                fontsInfo;
  776.     StandardFileReply            putReply;
  777.  
  778. /*
  779.     Display a StandardPutFile dialog and get the location to
  780.     save to.
  781. */
  782.  
  783.     StandardPutFile("\pPostScript® file to create:",
  784.                     "\pUntitled Print File", &putReply);
  785.  
  786.     require_action(putReply.sfGood, GotAnError, err = gxPrUserAbortErr;);
  787.  
  788. /*
  789.     If the user didn't cancel, save the file location, the
  790.     print destination (to disk), the file format name, and
  791.     the font inclusion info.
  792. */
  793.  
  794.     BlockMove(&putReply.sfFile, &fileLocInfo.fileSpec, sizeof(FSSpec));
  795.     StoreJobCollectionItem(&fileLocInfo, sizeof(gxFileLocationInfo),
  796.                            gxFileLocationTag, gxPrintingTagID);
  797.  
  798.     destInfo.toFile = true;
  799.     StoreJobCollectionItem(&destInfo, sizeof(gxFileDestinationInfo),
  800.                            gxFileDestinationTag, gxPrintingTagID);
  801.  
  802.     BlockMove("PostScript®", &destFFmt.fileFormatName[1], 11);
  803.     destFFmt.fileFormatName[0] = 11;
  804.     StoreJobCollectionItem(&destFFmt, sizeof(gxFileFormatInfo),
  805.                            gxFileFormatTag, gxPrintingTagID);
  806.  
  807.     fontsInfo.includeFonts = gxIncludeNoFonts;
  808.     StoreJobCollectionItem(&fontsInfo, sizeof(gxFileFontsInfo),
  809.                            gxFileFontsTag, gxPrintingTagID);
  810. GotAnError:
  811.  
  812.     return err;
  813. }
  814.  
  815.  
  816. /*    ______________________________________________________________
  817.  
  818.     DriverPrValidate -
  819.     
  820.     This routine is a total override of the gxPrValidate message.
  821.     In here, we set up our default settings if we have an
  822.     uninitialized or unknown print record.
  823.  
  824.     WHY THIS IS IMPORTANT:
  825.     
  826.     If you don't override this message, the default PostScript
  827.     implementation will validate your print records for you.  If
  828.     you're using a different wDev, then the default implementation
  829.     will keep changing your print record as soon as it notices
  830.     your wDev.  You don't need to completely override this message,
  831.     but if your wDev is different than the LaserWriter driver's
  832.     you'll have to massage it before and after you forward the
  833.     message.
  834.  
  835.     The implemenation below assumes that we've overriden
  836.     gxPrintDefault so that our default print record is read in.
  837.  
  838.     ______________________________________________________________    */
  839.  
  840. OSErr DriverPrValidate(THPrint printHdl, Boolean *changedPtr)
  841. {
  842.     short        deviceWord, version;
  843.     Boolean        changed = true;
  844.     OSErr        err = noErr;
  845.  
  846. /*
  847.     If the high byte of the wDev for our driver and this print
  848.     record don't agree, call PrintDefault to load our default
  849.     print record.  Note that we don't check the version number
  850.     here, but you may want to do that as well.
  851.     
  852.     If the caller didn't pass us nil, return a flag indicating
  853.     whether or not we changed the print record.
  854. */
  855.  
  856.     deviceWord = (*printHdl)->prStl.wDev;
  857.     version = (*printHdl)->iPrVersion;
  858.     
  859.     if ((deviceWord >> 8) != kDeviceByte)
  860.         PrintDefault(printHdl);
  861.     else
  862.         changed = false;
  863.  
  864.     if (changedPtr != nil)
  865.         *changedPtr = changed;
  866.     
  867.     return err;
  868. }
  869.  
  870.  
  871. /*    ______________________________________________________________
  872.  
  873.     DriverPrintDefault -
  874.     
  875.     This routine is a total override of the gxPrintDefault message.
  876.     In here, we copy our default settings into the passed print
  877.     handle.
  878.  
  879.     WHY THIS IS IMPORTANT:
  880.     
  881.     You need to override this message so that your gxPrValidate
  882.     override and other callers can get your driver's default print
  883.     record when they need it.  This is best handled as a total
  884.     override, since the driver writer should specify what the
  885.     driver's default values are.
  886.  
  887.     ______________________________________________________________    */
  888.  
  889. OSErr DriverPrintDefault(THPrint printHdl)
  890. {
  891.     OSErr    err = noErr;
  892.     short    oldResFile;
  893.     Handle    defaultPrintRec;
  894.  
  895. /*
  896.     Save our old resource file, use our driver's resource file,
  897.     and load our default 'PREC' resource.  Copy that data into
  898.     our print handle, release the resource, and restore the
  899.     original resource file to the top of the chain before exiting.
  900. */
  901.  
  902.     oldResFile = CurResFile();
  903.     UseResFile(GXGetMessageHandlerResFile());
  904.  
  905.     defaultPrintRec = GetResource('PREC', 0);
  906.     require_action(defaultPrintRec, GetResource_Failed, err = resNotFound;);
  907.  
  908.     BlockMove(*defaultPrintRec, *printHdl, GetHandleSize(defaultPrintRec));
  909.     ReleaseResource(defaultPrintRec);
  910.  
  911. GetResource_Failed:
  912.     
  913.     UseResFile(oldResFile);
  914.     
  915.     return err;
  916. }
  917.  
  918. /*    ______________________________________________________________ */
  919.  
  920. enum {        // wdev
  921.     kFontSubstitution     = 0x0001,        
  922.     kPortrait             = 0x0002,
  923.     kGraphicSmoothing    = 0x0004,    
  924.     kTextSmoothing        = 0x0020
  925. };
  926.  
  927. enum {        // printX[0]
  928.     kColorMode             = 0x0008        
  929. };
  930.  
  931. enum {        // printX[5]
  932.     kInvertPage         = 0x0001,        
  933.     kFlipPageHoriz        = 0x0002,
  934.     kFlipPageVert        = 0x0004,    
  935.     kPreciseBitmap        = 0x0010,
  936.     kLargerPrintArea    = 0x0020,
  937.     kDownloadFonts        = 0x0080,
  938.     kPlus90                = 0x1000
  939. };
  940.  
  941. /*    ______________________________________________________________
  942.  
  943.     DriverConvertPrintRecordTo -
  944.     
  945.     This routine is a total override of the gxConvertPrintRecordTo
  946.     message. In here, we translate the passed old-world print
  947.     record into a universal print record.  This routine is the
  948.     opposite of DriverConvertPrintRecordFrom.
  949.  
  950.     WHY THIS IS IMPORTANT:
  951.     
  952.     This is how we pass data between the old and new printing
  953.     architectures.  You normally totally override this message
  954.     since you should be the authority on where your driver stores
  955.     which settings.
  956.  
  957.     ______________________________________________________________    */
  958.  
  959. OSErr DriverConvertPrintRecordTo(THPrint aPrintHdl)
  960. {
  961.     gxUniversalPrintRecord        *univRecPtr;
  962.     TPrint                        *printRecPtr;
  963.  
  964. /*
  965.     This driver's print record already matches up pretty
  966.     well to the universal print record.  Only the fields
  967.     below need to be mapped.
  968. */
  969.     
  970.     univRecPtr = *(gxUniversalPrintRecordHdl) aPrintHdl;
  971.     printRecPtr = *aPrintHdl;
  972.  
  973.  
  974. /*
  975.     We move the orientation, "print to file" and "manual feed"
  976.     settings from their private locations to their universal
  977.     locations.  Note that we also update our devType, because
  978.     universal print records always have $A9 in the high byte
  979.     of their wDev.
  980. */
  981.     
  982.     univRecPtr->orientation = (printRecPtr->prStl.wDev & 0x2)? gxPortraitOrientation :gxLandscapeOrientation;
  983.     univRecPtr->saveFile  = (printRecPtr->prXInfo.bUlOffset == 0)? 0 :1;
  984.  
  985.     univRecPtr->feed = (printRecPtr->prStl.feed == 3)?  gxManualFeed :gxAutoFeed;
  986.     univRecPtr->devType = (univRecPtr->devType & 0xFF) | 0xA900;
  987.     univRecPtr->coverPage = printRecPtr->prXInfo.iBandV;
  988.  
  989.     univRecPtr->options = 0;
  990.  
  991.     if (printRecPtr->prStl.wDev & kFontSubstitution) 
  992.         univRecPtr->options |= gxFontSubstitution;    // font substitution
  993.     if (printRecPtr->prStl.wDev & kGraphicSmoothing) 
  994.         univRecPtr->options |= gxGraphicSmoothing;    // graphic smoothing (LW)
  995.     if (printRecPtr->prStl.wDev & kTextSmoothing) 
  996.         univRecPtr->options |= gxTextSmoothing;        // text smoothing (SC)
  997.  
  998.     if (printRecPtr->printX[0] & kColorMode) 
  999.         univRecPtr->options |= gxColorMode;            // color printing
  1000.  
  1001.     if (printRecPtr->printX[5] & kInvertPage) 
  1002.         univRecPtr->options |= gxInvertPage;        // b/w invert image;
  1003.     if (printRecPtr->printX[5] & kFlipPageHoriz) 
  1004.         univRecPtr->options |= gxFlipPageHoriz;        // flip horizontal
  1005.     if (printRecPtr->printX[5] & kFlipPageVert) 
  1006.         univRecPtr->options |= gxFlipPageVert;        // flip vertical
  1007.     if (printRecPtr->printX[5] & kPreciseBitmap) 
  1008.         univRecPtr->options |= gxPreciseBitmap;        // tall adjusted (IW), precise bitmap (LW, SC)
  1009.     if (printRecPtr->printX[5] & kLargerPrintArea) 
  1010.         univRecPtr->options |= gxBiggerPages;        // no gaps (IW), larger print area (LW)
  1011.  
  1012.     if (false) 
  1013.         univRecPtr->options |= gxBidirectional;        // bidirectional printing
  1014.     if (false) 
  1015.         univRecPtr->options |= gxUserFlag2;            // user flag 2
  1016.  
  1017.     return noErr;
  1018. }
  1019.  
  1020.  
  1021. /*    ______________________________________________________________
  1022.  
  1023.     DriverConvertPrintRecordFrom -
  1024.     
  1025.     This routine is a total override of the gxConvertPrintRecordFrom
  1026.     message. In here, we translate the passed universal print record
  1027.     into an old-world print record.  This routine is the opposite of
  1028.     DriverConvertPrintRecordTo.
  1029.  
  1030.     WHY THIS IS IMPORTANT:
  1031.     
  1032.     This is how we pass data between the old and new printing
  1033.     architectures.  You normally totally override this message
  1034.     since you should be the authority on where your driver stores
  1035.     which settings.
  1036.  
  1037.     ______________________________________________________________    */
  1038.  
  1039. OSErr DriverConvertPrintRecordFrom(THPrint aPrintHdl)
  1040. {
  1041.     gxUniversalPrintRecord        *univRecPtr;
  1042.     TPrint                        *printRecPtr;
  1043.  
  1044. /*
  1045.     This driver's print record already matches up pretty
  1046.     well to the universal print record.  Only the fields
  1047.     below need to be mapped back to the old style print
  1048.     record.
  1049. */
  1050.     
  1051.     univRecPtr = *(gxUniversalPrintRecordHdl) aPrintHdl;
  1052.     printRecPtr = *aPrintHdl;
  1053.  
  1054. /*
  1055.     We move the orientation, "print to file" and "manual feed"
  1056.     settings from their universal locations to their private
  1057.     ones.  Note that we also update our WDev, because
  1058.     universal print records always have $A9 in the high byte
  1059.     of their wDev, and we need our device ID there.
  1060. */
  1061.  
  1062.     if (univRecPtr->orientation == gxPortraitOrientation)
  1063.         printRecPtr->prStl.wDev |= 0x2;
  1064.     else
  1065.         printRecPtr->prStl.wDev &= ~0x2;
  1066.  
  1067.     printRecPtr->prStl.feed = (univRecPtr->feed == gxManualFeed)? 3: 2;
  1068.     printRecPtr->prXInfo.bUlOffset = (univRecPtr->saveFile)? 1: 0;
  1069.  
  1070.     if (univRecPtr->options & gxFontSubstitution)    // font substitution
  1071.         printRecPtr->prStl.wDev |= kFontSubstitution;
  1072.     if (univRecPtr->options & gxGraphicSmoothing)    // graphic smoothing (LW)
  1073.         printRecPtr->prStl.wDev |= kGraphicSmoothing;
  1074.     if (univRecPtr->options & gxTextSmoothing)        // text smoothing (SC)
  1075.         printRecPtr->prStl.wDev |= kTextSmoothing;
  1076.  
  1077.     printRecPtr->prStl.wDev = (printRecPtr->prStl.wDev & 0xFF) | (kDeviceByte << 8);
  1078.                 
  1079.     if (univRecPtr->options & gxInvertPage)            // b/w invert image;
  1080.         printRecPtr->printX[5] |= kInvertPage;
  1081.     if (univRecPtr->options & gxFlipPageHoriz)        // flip horizontal
  1082.         printRecPtr->printX[5] |= kFlipPageHoriz;
  1083.     if (univRecPtr->options & gxFlipPageVert)        // flip vertical
  1084.         printRecPtr->printX[5] |= kFlipPageVert;
  1085.     if (univRecPtr->options & gxPreciseBitmap)        // tall adjusted (IW), precise bitmap (LW, SC)
  1086.         printRecPtr->printX[5] |= kPreciseBitmap;
  1087.     if (univRecPtr->options & gxBiggerPages)        // no gaps (IW), larger print area (LW)
  1088.         printRecPtr->printX[5] |= kLargerPrintArea;
  1089.  
  1090.     printRecPtr->prXInfo.iBandV = univRecPtr->coverPage;    
  1091.  
  1092. /*
  1093.     explicitly need to set fields in prXinfo part of univ to 0 or they will affect printrec,
  1094.     the following assumes that we don't ever care about whats in bPatScale, bUITThick, bUIOffset, etc
  1095. */
  1096.     univRecPtr->orientation = 0;                    // so iDevBytes doesn't get set
  1097.     univRecPtr->qualityMode = 0;
  1098.     univRecPtr->coverPage     = 0;                    // so iBands doesn't gets set to 1
  1099.     univRecPtr->firstTray     = 1;                    // as thats what LW does.
  1100.  
  1101.     return noErr;
  1102. }
  1103.  
  1104.  
  1105. /*    ______________________________________________________________
  1106.  
  1107.     StoreJobCollectionItem -
  1108.     
  1109.     This routine is a generic routine that stores a collection
  1110.     item in the current job's collection.  If the item already
  1111.     exists it is replaced.
  1112.  
  1113.     WHY THIS IS IMPORTANT:
  1114.     
  1115.     It's not.  This is just a utility routine.
  1116.  
  1117.     ______________________________________________________________    */
  1118.  
  1119. OSErr StoreJobCollectionItem(void *collectItem, long collectSize,
  1120.                              OSType collectType, long collectID)
  1121. {
  1122.     OSErr            err;
  1123.     Collection        jobCollection;
  1124.     long            index, itemSize, attributes;
  1125.  
  1126. /*
  1127.     Get the job collection and add the new data.  If the item
  1128.     already exists and is locked, replace it.
  1129. */
  1130.  
  1131.     jobCollection = GXGetJobCollection(GXGetJob());
  1132.  
  1133.     err = AddCollectionItem(jobCollection,
  1134.                             collectType,
  1135.                             collectID,
  1136.                             collectSize,
  1137.                             collectItem);
  1138.  
  1139.     if (err == collectionItemLockedErr)
  1140.     {
  1141.         err = GetCollectionItemInfo(jobCollection,
  1142.                                     collectType,
  1143.                                     collectID,
  1144.                                     &index,
  1145.                                     &itemSize,
  1146.                                     &attributes);
  1147.         if (!err)
  1148.             err = ReplaceIndexedCollectionItem(jobCollection,
  1149.                                                index,
  1150.                                                collectSize,
  1151.                                                collectItem);
  1152.     }
  1153.  
  1154.     return err;
  1155. }
  1156.  
  1157.  
  1158. /*    ______________________________________________________________
  1159.  
  1160.     ToggleControl -
  1161.     
  1162.     This routine toggles a control on or off.  It's useful for
  1163.     controls like checkboxes or radio buttons, which can only have
  1164.     a value of 0 or 1.  The function takes a "void **" so that we
  1165.     don't have to typecast the handle returned from GetDItem before
  1166.     calling this routine.
  1167.  
  1168.     WHY THIS IS IMPORTANT:
  1169.     
  1170.     It's not.  This is just a utility routine.
  1171.  
  1172.     ______________________________________________________________    */
  1173.  
  1174. void ToggleControl(void **itemH)
  1175. {
  1176.     short ctlVal = GetCtlValue((ControlHandle) itemH);
  1177.  
  1178.     ctlVal = (ctlVal == 1)? 0: 1;
  1179.     SetCtlValue((ControlHandle) itemH, ctlVal);
  1180. }
  1181.  
  1182.  
  1183. /*    ______________________________________________________________
  1184.  
  1185.     SetTheControlValue -
  1186.     
  1187.     This routine sets the passed control's value, if necessary.  It
  1188.     will not set a control to a value it's already set to, and that
  1189.     avoids flashing.  The function takes a "void **" so that we
  1190.     don't have to typecast the handle returned from GetDItem before
  1191.     calling this routine.
  1192.  
  1193.     WHY THIS IS IMPORTANT:
  1194.     
  1195.     It's not.  This is just a utility routine.
  1196.  
  1197.     ______________________________________________________________    */
  1198.  
  1199. void SetTheControlValue(void **itemH, short desiredValue)
  1200. {
  1201.     short ctlVal = GetCtlValue((ControlHandle) itemH);
  1202.  
  1203.     if (ctlVal != desiredValue)
  1204.         SetCtlValue((ControlHandle) itemH, desiredValue);
  1205. }
  1206.